<?php
// +----------------------------------------------------------------------
// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2017 http://www.thinkcmf.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------
// | Author: Dean <zxxjjforever@163.com>
// +----------------------------------------------------------------------
namespace cmf\controller;

use think\Config;
use think\Db;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Loader;
use think\Request;
use think\Response;

class RestBaseController {
	//token
	protected $token = '';

	//设备类型
	protected $deviceType = '';

	//用户 id
	protected $userId = 0;

	//用户
	protected $user;

	//用户类型
	protected $userType;

	protected $allowedDeviceTypes = ['mobile', 'android', 'iphone', 'ipad', 'web', 'pc', 'mac', 'wxapp'];

	/**
	 * @var \think\Request Request实例
	 */
	protected $request;
	// 验证失败是否抛出异常
	protected $failException = false;
	// 是否批量验证
	protected $batchValidate = false;

	/**
	 * 前置操作方法列表
	 * @var array $beforeActionList
	 * @access protected
	 */
	protected $beforeActionList = [];

	/**
	 * 架构函数
	 * @param Request $request Request对象
	 * @access public
	 */
	public function __construct(Request $request = null) {
		if (is_null($request)) {
			$request = Request::instance();
		}

		Request::instance()->root(cmf_get_root() . '/');

		$this->request = $request;

		// 用户验证初始化
		$this->_initUser();

		// 控制器初始化
		$this->_initialize();

		// 前置操作方法
		if ($this->beforeActionList) {
			foreach ($this->beforeActionList as $method => $options) {
				is_numeric($method) ?
				$this->beforeAction($options) :
				$this->beforeAction($method, $options);
			}
		}
	}

	// 初始化
	protected function _initialize() {
	}

	private function _initUser() {
		$token = $this->request->header('XX-Token');
		$deviceType = $this->request->header('XX-Device-Type');

		if (empty($token)) {
			return;
		}

		if (empty($deviceType)) {
			return;
		}

		if (!in_array($deviceType, $this->allowedDeviceTypes)) {
			return;
		}

		$this->token = $token;
		$this->deviceType = $deviceType;

		$user = Db::name('user_token')
			->alias('a')
			->field('b.*')
			->where(['token' => $token, 'device_type' => $deviceType])
			->join('__USER__ b', 'a.user_id = b.id')
			->find();

		if (!empty($user)) {
			$this->user = $user;
			$this->userId = $user['id'];
			$this->userType = $user['user_type'];
		}

	}

	/**
	 * 前置操作
	 * @access protected
	 * @param string $method 前置操作方法名
	 * @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]]
	 */
	protected function beforeAction($method, $options = []) {
		if (isset($options['only'])) {
			if (is_string($options['only'])) {
				$options['only'] = explode(',', $options['only']);
			}
			if (!in_array($this->request->action(), $options['only'])) {
				return;
			}
		} elseif (isset($options['except'])) {
			if (is_string($options['except'])) {
				$options['except'] = explode(',', $options['except']);
			}
			if (in_array($this->request->action(), $options['except'])) {
				return;
			}
		}

		call_user_func([$this, $method]);
	}

	/**
	 * 设置验证失败后是否抛出异常
	 * @access protected
	 * @param bool $fail 是否抛出异常
	 * @return $this
	 */
	protected function validateFailException($fail = true) {
		$this->failException = $fail;
		return $this;
	}

	/**
	 * 验证数据
	 * @access protected
	 * @param array $data 数据
	 * @param string|array $validate 验证器名或者验证规则数组
	 * @param array $message 提示信息
	 * @param bool $batch 是否批量验证
	 * @param mixed $callback 回调方法（闭包）
	 * @return array|string|true
	 * @throws ValidateException
	 */
	protected function validate($data, $validate, $message = [], $batch = false, $callback = null) {
		if (is_array($validate)) {
			$v = Loader::validate();
			$v->rule($validate);
		} else {
			if (strpos($validate, '.')) {
				// 支持场景
				list($validate, $scene) = explode('.', $validate);
			}
			$v = Loader::validate($validate);
			if (!empty($scene)) {
				$v->scene($scene);
			}
		}
		// 是否批量验证
		if ($batch || $this->batchValidate) {
			$v->batch(true);
		}

		if (is_array($message)) {
			$v->message($message);
		}

		if ($callback && is_callable($callback)) {
			call_user_func_array($callback, [$v, &$data]);
		}

		if (!$v->check($data)) {
			if ($this->failException) {
				throw new ValidateException($v->getError());
			} else {
				return $v->getError();
			}
		} else {
			return true;
		}
	}

	/**
	 * 操作成功跳转的快捷方法
	 * @access protected
	 * @param mixed $msg 提示信息
	 * @param mixed $data 返回的数据
	 * @param array $header 发送的Header信息
	 * @return void
	 */
	protected function success($msg = '', $data = '', array $header = []) {
		$code = 1;
		$result = [
			'code' => $code,
			'msg' => $msg,
			'data' => $data,
		];

		$type = $this->getResponseType();
		$header['Access-Control-Allow-Origin'] = '*';
		$header['Access-Control-Allow-Headers'] = 'X-Requested-With,Content-Type,XX-Device-Type,XX-Token';
		$header['Access-Control-Allow-Methods'] = 'GET,POST,PATCH,PUT,DELETE,OPTIONS';
		$response = Response::create($result, $type)->header($header);
		throw new HttpResponseException($response);
	}

	/**
	 * 操作错误跳转的快捷方法
	 * @access protected
	 * @param mixed $msg 提示信息,若要指定错误码,可以传数组,格式为['code'=>您的错误码,'msg'=>'您的错误消息']
	 * @param mixed $data 返回的数据
	 * @param array $header 发送的Header信息
	 * @return void
	 */
	protected function error($msg = '', $data = '', array $header = []) {
		$code = 0;
		if (is_array($msg)) {
			$code = $msg['code'];
			$msg = $msg['msg'];
		}
		$result = [
			'code' => $code,
			'msg' => $msg,
			'data' => $data,
		];

		$type = $this->getResponseType();
		$header['Access-Control-Allow-Origin'] = '*';
		$header['Access-Control-Allow-Headers'] = 'X-Requested-With,Content-Type,XX-Device-Type,XX-Token';
		$header['Access-Control-Allow-Methods'] = 'GET,POST,PATCH,PUT,DELETE,OPTIONS';
		$response = Response::create($result, $type)->header($header);
		throw new HttpResponseException($response);
	}

	/**
	 * 获取当前的response 输出类型
	 * @access protected
	 * @return string
	 */
	protected function getResponseType() {
		return Config::get('default_return_type');
	}

	/**
	 * 获取当前登录用户的id
	 * @return int
	 */
	public function getUserId() {
		if (empty($this->userId)) {
			$this->error(['code' => 10001, 'msg' => '用户未登录']);
		}
		return $this->userId;

	}

}